package cn.coin.ox.test;

import cn.coin.ox.common.erc20.config.ETHConfig;
import cn.coin.ox.common.erc20.enums.ERC20ContractAddressEnums;
import cn.coin.ox.util.ETHUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.admin.methods.response.PersonalUnlockAccount;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.*;
import java.util.concurrent.ExecutionException;

public class ETFTestMain {

    private static Logger log = LoggerFactory.getLogger(ETFTestMain.class);

    public static void main(String[] args) throws Exception {
        String walletFileBasePath = "D:\\keystore\\\\";
        // 密码
        String password = "123456";
        String walletFileName = creatAccount(walletFileBasePath, password);
        // 钱包文件
        String walletFile = walletFileBasePath +walletFileName;
        System.out.println("第一步：创建的钱包KeyStore文件为: ---> " + walletFile);
        Credentials credentials = ETHUtil.getCredentials(walletFile, password);
        // 加载钱包
        Map<String, Object> map = ETHUtil.loadWallet(credentials);
        String  address = String.valueOf(map.get("address"));
        String  privateKey = String.valueOf(map.get("privateKey"));
        String  publicKey = String.valueOf(map.get("publicKey"));


        Web3j web3j = createETHclient(ETHConfig.API_INFURA_TOKEN);
        System.out.println("==================获取钱包余额");
        String blanceOf = getBlanceOf(web3j, address);
        System.out.println("当前账户 ETH 余额: " + blanceOf);

        // USDT ERC20 合约地址
        String contractAddress = ERC20ContractAddressEnums.USDT.getContractAddress();

        System.out.println("==================第一种获取USDT ERC20 余额");
        String erc20Balance = TestGetBalance.getERC20Balance(web3j, address, contractAddress);
        System.out.println("当前账户 USDT 余额: " + erc20Balance);

        System.out.println("==================开始交易");
        // 交易 转账至地址
        String toAddress = "0x94589ce9251bb25f4af83a43651ca99f7fd6bc9e";
        BigDecimal amount = new BigDecimal("10000"); // 0.01
        System.out.println("----------------- ERC10 合约");
        transto(web3j, credentials, amount, toAddress);
        System.out.println("----------------- ERC20 合约");
        String result = transferERC20Token(web3j, address, toAddress, new BigDecimal("3"), credentials, contractAddress);
        System.out.println(result);
//        TestGetBalance.transferToken(web3j, address, credentials, to, contractAddress, new BigDecimal(3));
//        transferERC20Token(web3j, address, to, new BigDecimal(3), credentials, contractAddress);
        System.out.println("==================第二种获取USDT ERC20 余额");
        BigDecimal balance = TestGetBalance.getBalance(web3j, address, contractAddress);
        System.out.println("当前账户USDT余额: " + balance);

    }

    /*************创建一个钱包文件**************/
    private static String creatAccount(String walletFileBasePath, String password)
            throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
        String walletFileName0 = "";//文件名
        //钱包文件保持路径，请替换位自己的某文件夹路径
        walletFileName0 = WalletUtils.generateNewWalletFile(password, new File(walletFileBasePath), false);
        return walletFileName0;
    }


    /*******连接以太坊客户端**************/
    private static Web3j createETHclient(String token) throws IOException {
        //连接方式1：使用infura 提供的客户端
        // TODO: 2018/4/10 token更改为自己的
//        Web3j web3j = Web3j.build(new HttpService("https://mainnet.infura.io/v3/"+token));
        // 测试环境
        Web3j web3j = Web3j.build(new HttpService("https://ropsten.infura.io/v3/"+token));
        //连接方式2：使用本地客户端
//        Web3j web3j = Web3j.build(new HttpService("127.0.0.1:7545"));
        //测试是否连接成功
        String web3ClientVersion = web3j.web3ClientVersion().send().getWeb3ClientVersion();
        log.info("version=" + web3ClientVersion);
        return web3j;
    }

    /***********查询指定地址的余额***********/
    private static String getBlanceOf(Web3j web3j, String address) throws IOException {
        if (web3j == null){
            return "";
        }
        //第二个参数：区块的参数，建议选最新区块
        EthGetBalance balance = web3j.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
        //格式转化 wei-ether
        String blanceETH = Convert.fromWei(balance.getBalance().toString(), Convert.Unit.ETHER).toPlainString().concat(" ether");
        log.info(blanceETH);
        return blanceETH;
    }

    /****************交易*****************/
    private static void transto(Web3j web3j, Credentials credentials, BigDecimal amount, String addressTo) throws Exception {
        if (web3j == null) return;
        if (credentials == null) return;
        //开始发送0.01 =eth到指定地址
        TransactionReceipt send = Transfer.sendFunds(web3j, credentials, addressTo, amount, Convert.Unit.SZABO).sendAsync().get();
        log.info("Transaction complete:");
        log.info("trans hash=" + send.getTransactionHash());
        log.info("from :" + send.getFrom());
        log.info("to:" + send.getTo());
        log.info("gas used=" + send.getGasUsed());
        log.info("status: " + send.getStatus());
    }


    static String TRANSFER = "transfer";

    /**
     * erc20代币转账
     *
     * @param from            转账地址
     * @param to              收款地址
     * @param value           转账金额
     * @param contractAddress 代币合约地址
     * @return 交易哈希
     */
    public static String transferERC20Token(Web3j web3j, String from, String to,
                                            BigDecimal value, Credentials credentials, String contractAddress)
            throws ExecutionException, InterruptedException, IOException {
        //获取nonce，交易笔数
        BigInteger nonce;
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.PENDING).send();
        if (ethGetTransactionCount == null) {
            return null;
        }
        nonce = ethGetTransactionCount.getTransactionCount();
        //gasPrice和gasLimit 都可以手动设置
        BigInteger gasPrice;
        EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get();
        if (ethGasPrice == null) {
            return null;
        }
        gasPrice = ethGasPrice.getGasPrice();
        //BigInteger.valueOf(4300000L) 如果交易失败 很可能是手续费的设置问题
        BigInteger gasLimit = BigInteger.valueOf(60000L);
        //ERC20代币合约方法
//        value = value.multiply(VALUE);
        final Uint256 uint256 = new Uint256(value.multiply(BigDecimal.TEN.pow(18)).toBigInteger());
        Function function = new Function(
                TRANSFER,
                Arrays.asList(new Address(to), uint256),
                Collections.singletonList(new TypeReference<Type>() {
                }));
        //创建RawTransaction交易对象
        String encodedFunction = FunctionEncoder.encode(function);
        RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit,
                contractAddress, encodedFunction);

        //签名Transaction
        byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signMessage);
        //发送交易
        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
        String hash = ethSendTransaction.getTransactionHash();
        if (hash != null) {
            System.out.println("交易成功:  HASH: " + hash);
            return hash;
        }
        return null;
    }


}
